#include <iostream>
#include <string>
#include <cstring>
#include <memory>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

const static int defaultsockfd = -1;
const static int defaultretryinterval = 1;
const static int defaultmaxretries = 5;

enum ExitCode
{
    USAGE_ERR = 1,
    SOCKET_ERR
};

enum class Status // C++11的强类型枚举
{
    NEW,          // 新建状态
    CONNECTING,   //正在连接
    CONNECTED,    //连接成功
    DISCONNECTED, //重连失败
    CLOSED        //连接失败，经历重连，无法连接
};

class ClientConnection
{
public:
    ClientConnection(const std::string& serverip, uint16_t serverport)
    :_sockfd(defaultsockfd),
    _serverip(serverip),
    _serverport(serverport),
    _status(Status::NEW),
    _retry_interval(defaultretryinterval), //重试的时间间隔
    _max_reties(defaultmaxretries)         //重试次数
    {}
    Status GetStatus()
    {
        return _status;
    }
    void Connect()
    {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)
        {
            std::cerr << "create sockfd error" << std::endl;
            exit(SOCKET_ERR);
        }
        struct sockaddr_in server;
        memset(&server, 0 , sizeof(server));
        server.sin_port = htons(_serverport);
        server.sin_family = AF_INET;
        inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr.s_addr);  
        int n = ::connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
        if(n < 0)
        {
            //1.关闭fd
            Disconnect();
            //2.更新状态
            _status = Status::DISCONNECTED;
            //3.返回
            return;
        }
        //连接成功，只需更新一下状态
        _status = Status::CONNECTED;
    }
    void Process()  //正常通信
    {
        while(true)
        {
            std::string message = "hello server";
            ssize_t n = ::send(_sockfd, message.c_str(), sizeof(message), 0);
            if(n < 0)
            {
                std::cerr << "send error" << std::endl;
                _status = Status::CLOSED;
                break;
            }
            char buffer[1024];
            ssize_t m = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if(m > 0)
            {
                buffer[m] = 0;
                std::cout << "server echo# " << buffer << std::endl;
            }
            else
            {
                _status = Status::DISCONNECTED;
                break;
            }
        }
    }
    void Reconnect()
    {
        _status = Status::CONNECTING;
        int cnt = 0;
        while(true)
        {
            Connect();
            if(_status == Status::CONNECTED)
            {
                break;
            }
            cnt++;
            if(cnt > _max_reties)
            {
                _status = Status::CLOSED;
                std::cout << "重连失败..., 请检查你的网络" << std::endl;  
                break;  
            }
            std::cout << "断线重连中，重连次数:" << cnt << std::endl;
            sleep(_retry_interval);
        }
    }
    void Disconnect()
    {
        if(_sockfd > defaultsockfd)
        {
            ::close(_sockfd);
            _sockfd = defaultsockfd;
            _status = Status::CLOSED;
        }
    }
    ~ClientConnection()
    {}
    private:
    int _sockfd;
    std::string _serverip;
    uint16_t _serverport;
    Status _status;
    int _retry_interval;  //重试的时间间隔
    int _max_reties;      //最大重试次数
    };

    class TcpClient
    {
    public:
    TcpClient(const std::string& serverip, uint16_t serverport):_connection(serverip, serverport)
    {}
    void Excute()
    {
        while(true)
        {
            switch(_connection.GetStatus())
            {
                case Status::NEW:
                    _connection.Connect();
                break;
                case Status::CONNECTED:
                    _connection.Process();
                break;
                case Status::DISCONNECTED:
                    _connection.Reconnect();
                break;
                case Status::CLOSED:
                    _connection.Disconnect();
                return;
                default:
                //Do Nothing
                break;
            }
        }
    }
    ~TcpClient()
    {}
private:
    ClientConnection _connection;
};

void Usage(std::string process)
{
    std::cout << "Usage: " << process << " serverip serverport" << std::endl; 
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    std::unique_ptr<TcpClient> tcpclient = std::make_unique<TcpClient>(serverip, serverport);
    tcpclient->Excute();
    return 0;
}